#include "czDisplay.h"

#include <InputListener.h>

#include <assert.h>
#include <stdio.h>

#include "ConTLN.h"

#include <stdio.h>
#include <vector>

enum EType {
  ET_PRESSED,
  ET_RELEASED
};
enum SType {
  ST_UP,
  ST_DOWN
};

typedef struct KeyState_tag {
  EType s;
  SType k;
  char code;
} KeyState;

typedef std::vector<KeyState> KeyStateTable;
KeyStateTable ksTable;

static char keysA[KEY_NUMBER]={0};
static char keysB[KEY_NUMBER]={0};
static char* keys = keysA, * keysLast = keysB;

void SwapKeyBuff()
{
  if(keys == keysA)
  {
    keys = keysB;
    keysLast = keysA;
  }else{
    keys = keysA;
    keysLast = keysB;
  }
}

typedef std::vector<char> BindKeyTable;
BindKeyTable bindK;

void BindEvent()
{
  bindK.push_back('q');
  bindK.push_back('j');

}

void UpdateEvent()
{
  KeyState ks;
  for(int i=0; i<bindK.size(); ++i)
  {
    if( !keysLast[bindK[i]] && keys[bindK[i]] )
    {
      ks.s = ET_PRESSED;
      ks.k = ST_DOWN;
      ks.code = bindK[i];
      ksTable.push_back(ks);
    }
    if( keysLast[bindK[i]] && !keys[bindK[i]] )
    {
      ks.s = ET_RELEASED;
      ks.k = ST_UP;
      ks.code = bindK[i];
      ksTable.push_back(ks);
    }
  }
}

WindowCanmore::~WindowCanmore()
{
  close();
}

bool WindowCanmore::open(Context* eglContext, InputListener* listener, const char title[], int width, int height )
{
  // setup data
  this->eglContext = eglContext;
  this->width = width;
  this->height = height;

  // defaults

  run = true;
  //window = 0;
  active = true;
  _listener = listener;
  centered = true;
  mode = Fullscreen;

  // setup window
  window = GDL_PLANE_ID_UPP_C;
  gdl_init(0);

  gdl_pixel_format_t pix_fmt = GDL_PF_ARGB_32;
  gdl_color_space_t color_space = GDL_COLOR_SPACE_RGB;
  gdl_rectangle_t src_rect;
  gdl_rectangle_t dst_rect;
  gdl_ret_t rc;

  dst_rect.origin.x = 0;
  dst_rect.origin.y = 0;
  dst_rect.width = width;
  dst_rect.height = height;

  src_rect.origin.x = 0;
  src_rect.origin.y = 0;
  src_rect.width = width;
  src_rect.height = height;

#ifdef _LINUX_CANMORE
  rc = gdl_plane_reset(window);
  if (GDL_SUCCESS != rc)
  {
    printf("gdl_plane_reset failed!\n");
    return false;
  }
#endif

  rc = gdl_plane_config_begin(window);
  if (GDL_SUCCESS != rc)
  {
    printf("gdl_plane_config_begin failed!\n");
    return false;
  }

  rc = gdl_plane_set_attr(GDL_PLANE_SRC_COLOR_SPACE, &color_space);
  if (GDL_SUCCESS != rc)
  {
    printf("gdl_plane_set_attr(GDL_PLANE_SRC_COLOR_SPACE) failed!\n");
    return false;
  }

  rc = gdl_plane_set_attr(GDL_PLANE_PIXEL_FORMAT, &pix_fmt);
  if (GDL_SUCCESS != rc)
  {
    printf("gdl_plane_set_attr(GDL_PLANE_PIXEL_FORMAT) failed!\n");
    return false;
  }

  rc = gdl_plane_set_attr(GDL_PLANE_DST_RECT, &dst_rect);
  if (GDL_SUCCESS != rc)
  {
    printf("gdl_plane_set_attr(GDL_PLANE_DEST_RECT) failed!\n");
    return false;
  }

  rc = gdl_plane_set_attr(GDL_PLANE_SRC_RECT, &src_rect);
  if (GDL_SUCCESS != rc)
  {
    gdl_plane_config_end(GDL_TRUE);
    printf("gdl_plane_set_attr(GDL_PLANE_SRC_RECT) failed!\n");
    return false;
  }

  rc = gdl_plane_config_end(GDL_FALSE);
  if (GDL_SUCCESS != rc)
  {
    printf("gdl_plane_config_end failed!\n");
    return false;
  }

  if(eglContext)
  {
    if( !eglContext->init(this) );
      return false;
  }

  StoreEnv();

  BindEvent();

  if(_listener)
    _listener->onOpen(*this);

  return true;
}

void WindowCanmore::close()
{
  if(_listener) {
    _listener->onClose(*this);
    _listener = 0;
  }
  RestoreEnv();

  if(eglContext)
  {
    eglContext->shutdown();
    eglContext = 0;
  }

  gdl_close();
  //window = 0;
}

// show the window (it is initially hidden)

void WindowCanmore::show()
{
  if(_listener)
    _listener->onActivate(*this, true);

}

// hide the window

void WindowCanmore::hide()
{
  if(_listener)
    _listener->onActivate(*this, false);
}

// check if window is visible?

bool WindowCanmore::visible() const
{
  return true;
}

// put window in fullscreen mode

void WindowCanmore::fullscreen(int width, int height)
{
}

// put window in windowed mode

void WindowCanmore::windowed( int width, int height )
{
}

// center the window on the desktop
// note: has no effect if the window is minimized or maximized

void WindowCanmore::center()
{
  centered = true;
}

// zoom window

void WindowCanmore::zoom( float scale )
{
}

bool WindowCanmore::isRun() const
{
  return run;
}
// window update pumps the message queue

void WindowCanmore::update()
{
  if(eglContext)
    eglContext->update();

  KB_UpdateStatus(keys);

  UpdateEvent();
  ProcessEventAll();
  ProcessUnEventAll();

  SwapKeyBuff();

  if(_listener)
    _listener->defaultKeyHandlers();
}

// get the window handle.
// null if the window failed to initialize.

WindowHandle WindowCanmore::handle() const
{
  return window;
}

int WindowCanmore::getWidth() const { return width; }
int WindowCanmore::getHeight() const { return height; }
bool WindowCanmore::getActive() const { return active; }

Mode WindowCanmore::getOutputMode() const { return mode; }

// title management

void WindowCanmore::title( const char title[] )
{

}

// listener management

InputListener * WindowCanmore::listener() const
{
  return _listener;
}




Key Code2Key(char code)
{
  return Key();
}

void WindowCanmore::ProcessEventAll()
{
  KeyStateTable::iterator it = ksTable.begin();
  for(; it != ksTable.end(); ++it)
  {
    KeyState& event = *it;
    if( event.s == ET_PRESSED )
    {
      Key key = Code2Key(event.code); // ???
      _listener->onKeyPressed(*this, key);
    }else if(event.s == ET_RELEASED)
    {
      Key key = Code2Key(event.code); // ???
      _listener->onKeyUp(*this, key);
    }
  }
  ksTable.clear();
}

void WindowCanmore::ProcessUnEventAll()
{
  BindKeyTable::iterator it = bindK.begin();
  for(; it != bindK.end(); ++it)
  {
    if( KB_CheckKey(*it) )
    {
      _listener->onKeyDown(*this, Code2Key(*it));
    }else{
      _listener->onKeyUp(*this, Code2Key(*it));
    }
  }
}
